package com.fan.aspect;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.json.JSONUtil;
import com.fan.utils.SignUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author D-BJ-144
 * @description SignAspect
 * @Date 2025/2/14 13
 */
@Slf4j
@Aspect
@Component
public class SignAspect {
    private static Map<String, String> nonceMap = new ConcurrentHashMap<>();

    @Before("@annotation(signatureValidation)")
    public void doBefore(SignatureValidation signatureValidation) throws Throwable {
        // 接收到请求，记录请求内容
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

        // 1、获取请求sign签名参数，
        String sign = request.getHeader("sign");
        if (StrUtil.isBlank(sign)) {
            throw new RuntimeException("sign不能为空");
        }
        // 2、获取请求参数secret
        String appId = request.getHeader("appId");
        String appSecret = getAppSecret(appId);
        if (StrUtil.isBlank(appSecret)) {
            throw new RuntimeException("appId不合法");
        }
        // 3、获取请求参数timestamp 时间戳，
        String timestamp = request.getHeader("timestamp");
        if (StrUtil.isBlank(timestamp)) {
            throw new RuntimeException("timestamp不能为空");
        }
        //3. 比较时间，120s内为合法请求
        if (Math.abs(Long.parseLong(timestamp) - System.currentTimeMillis()) > 120000) {
            throw new RuntimeException("timestamp失效");
        }
        // 4、验证重复提交问题nonce
        String nonce = request.getHeader("nonce");
        String nonce_old = nonceMap.get(nonce);
        if (StrUtil.isNotBlank(nonce_old)) {
            throw new RuntimeException("请不要重复提交");
        }
        String paramStr = extractRequestParams(request);
        verifySign(appSecret,sign,paramStr);
    }

    @SuppressWarnings("unchecked")
    public String extractRequestParams(HttpServletRequest request) {
        // @RequestBody
        String body = null;
        // 验签逻辑不能放在拦截器中，因为拦截器中不能直接读取body的输入流，否则会造成后续@RequestBody的参数解析器读取不到body
        // 由于body输入流只能读取一次，因此需要使用ContentCachingRequestWrapper包装请求，缓存body内容，但是该类的缓存时机是在@RequestBody的参数解析器中
        // 因此满足2个条件才能使用ContentCachingRequestWrapper中的body缓存
        // 1. 接口的入参必须存在@RequestBody
        // 2. 读取body缓存的时机必须在@RequestBody的参数解析之后，比如说：AOP、Controller层的逻辑内。注意拦截器的时机是在参数解析之前的
        if (request instanceof ContentCachingRequestWrapper) {
            ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) request;
            body = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
        }

        // @RequestParam
        Map<String, String[]> paramMap = request.getParameterMap();

        // @PathVariable
        ServletWebRequest webRequest = new ServletWebRequest(request, null);
        Map<String, String> uriTemplateVarNap = (Map<String, String>) webRequest.getAttribute(
                HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);

        return SignUtil.extractRequestParams(body, paramMap, uriTemplateVarNap);
    }

    private void verifySign(String appSecret, String sign, String paramStr) {
        // 对方签名
        String format = StrUtil.format("{}&appSecret={}", paramStr, appSecret);
        String mySign = SecureUtil.md5(format).toUpperCase();

         log.info("验签，对方签名：{}，我方签名：{}", sign, mySign);
        System.out.println(StrUtil.format("验签结果：{}，对方签名：{}，我方签名：{}",sign.equals(mySign), sign, mySign));
        // 验签
        // Assert.isTrue(StrUtil.equals(sign, mySign), "验签失败");
    }

    public String getAppSecret(String appId) {
        Map<String, String> map = new HashMap<>();

        map.put("zs001", "asd123fhg3b7fgh7dfg");
        map.put("ls001", "hghfgh123btgfyh1212");

        return map.get(appId);
    }
    public String getNonce(String nonce) {
        Map<String, String> map = new HashMap<>();

        map.put("zs001", "asd123fhg3b7fgh7dfg");
        map.put("ls001", "hghfgh123btgfyh1212");

        return map.get(nonce);
    }
}

